package com.hb.trade.api.routine.service.tenderdoc;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.hb.core.pojo.Attachment;
import com.hb.core.pojo.Attachments;
import com.hb.trade.api.routine.util.CommonUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.fzzn.core.service.ZipFileService;

import java.io.File;
import java.util.List;
import java.util.Set;

/**
 * 法正智能科技有限公司
 * 由投标文件编制工具加密后的投标文件操作接口
 *
 * @author zhaojk
 */
@Slf4j
@Setter
@Getter
public abstract class AbstractTenderDocumentOption {

    /**
     * tenderDoc.pdf - 未签章的投标书在压缩包中的文件名
     */
    public final static String TENDER_DOC = "tenderDoc.pdf";

    /**
     * tenderDocSignature.pdf - 签过章的投标书在压缩包中的文件名
     */
    public final static String TENDER_DOC_SIGNATURE = "tenderDocSignature.pdf";

    /**
     * 标段ID的存放位置
     */
    protected final static String SECTION_ID_LOCATION = "Message.bidId";

    /**
     * 数字信封文件名
     */
    protected final static String DIGITAL_ENVELOPE_NAME = "digitalEnvelopexml.xml";

    /**
     * 加密的投标文件名称
     */
    protected final static String ENC_TENDER_FILE_NAME = "tbwj.enc";

    /**
     * 开标一览表文件名称
     */
    protected final static String OPENING_SCHEDULE_NAME = "fileKBYLxml.xml";

    /**
     * 加密的物理文件对象
     */
    protected File file;

    /**
     * 解密后的物理文件对象
     */
    protected File decFile;

    /**
     * 数字信封json对象
     */
    protected JSONObject digitalEnvelopeJSON;

    /**
     * 投标文件必须基于至少一个文件
     *
     * @param attachment
     */
    public AbstractTenderDocumentOption(Attachment attachment) {
        if (attachment == null) {
            return;
        }
        File fixedFile = attachment.getFixedFile("zip");
        if (!FileUtil.exist(fixedFile)) {
            return;
        }
        file = fixedFile;
    }

    /**
     * 投标文件必须基于至少一个文件
     * 若传入集合, 则只取第一个文件
     *
     * @param attachments
     */
    public AbstractTenderDocumentOption(Attachments attachments) {
        if (CollUtil.isEmpty(attachments)) {
            return;
        }
        Attachment attachment = attachments.get(0);
        if (attachment == null) {
            return;
        }
        File fixedFile = attachment.getFixedFile("zip");
        if (!FileUtil.exist(fixedFile)) {
            return;
        }
        file = fixedFile;
    }

    /**
     * 验证投标文件
     * 20220304投标文件递交失败提示信息统一改为 "递交失败, 请上传加密后的投标文件"
     *
     * @throws Exception
     */
    public void validate() throws Exception {
        if (!FileUtil.exist(file)) {
//            throw new RuntimeException("没有可验证的文件");
            throw new RuntimeException("递交失败, 请上传加密后的投标文件");
        }

        // 读取投标文件
        ZipFileService instance = ZipFileService.getInstance(file);

        log.info("加载投标文件成功, 开始验证...");
        // 读取数字信封
        File itemFile = instance.getItemFile(DIGITAL_ENVELOPE_NAME);
        if (FileUtil.exist(itemFile)) {
            log.info("数字信封存在");
        } else {
//            throw new Exception("缺少数字信封");
            throw new RuntimeException("递交失败, 请上传加密后的投标文件");
        }

        // 将投标文件数字信封中的内容以json的格式返回
        JSONObject digitalEnvelope = CommonUtil.xmlFileToJSON(itemFile);
        if (CollUtil.isNotEmpty(digitalEnvelope)) {
            log.info("数字信封内容已加载");
        } else {
//            throw new Exception("数字信封内容丢失");
            throw new RuntimeException("递交失败, 请上传加密后的投标文件");
        }

        Long sectionId = digitalEnvelope.getByPath(SECTION_ID_LOCATION, Long.class);
        if (sectionId != null) {
            log.info("投标文件对应的标段ID: {}", sectionId);
        } else {
//            throw new Exception("未能获取投标文件中的标段信息");
            throw new RuntimeException("递交失败, 请上传加密后的投标文件");
        }

        // 读取加密的投标文件本体
        itemFile = instance.getItemFile(ENC_TENDER_FILE_NAME);
        if (FileUtil.exist(itemFile)) {
            log.info("投标文件存在(加密后)");
        } else {
//            throw new Exception("投标文件缺失");
            throw new RuntimeException("递交失败, 请上传加密后的投标文件");
        }
        log.info("验证结束");

        this.digitalEnvelopeJSON = digitalEnvelope;
    }

    /**
     * 获取投标文件中的标段ID
     *
     * @return
     */
    public Long getSectionId() {
        if (CollUtil.isEmpty(digitalEnvelopeJSON)) {
            try {
                validate();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
        return digitalEnvelopeJSON.getByPath(SECTION_ID_LOCATION, Long.class);
    }

    /**
     * 解密投标文件
     *
     * @param encStr base64加密过的对称密钥
     */
    public abstract void decryptBase64(String encStr) throws Exception;

    /**
     * 解密投标文件
     *
     * @param encStr 对称密钥
     */
    public abstract void decrypt(String encStr) throws Exception;

    /**
     * 获取解密后投标文件的开标一览表内容
     *
     * @return
     */
    public JSONObject getOpeningSchedule() {
        if (!FileUtil.exist(decFile)) {
            log.error("投标文件不存在或未解密, file: {}", decFile.getAbsolutePath());
            return null;
        }
        ZipFileService zipFileService;
        try {
            log.info("读取开标一览表({}): {}", OPENING_SCHEDULE_NAME, decFile.getAbsolutePath());
            zipFileService = ZipFileService.getInstance(decFile);

            // 开标一览表的xml
            File itemFile = zipFileService.getItemFile(OPENING_SCHEDULE_NAME);
            if (FileUtil.exist(itemFile)) {
                return renderSchedule(itemFile);
            }
        } catch (Exception e) {
            log.error("读取开标一览表异常", e);
        }

        return JSONUtil.createObj();
    }

    /**
     * 组装xml中开标一览表对象
     *
     * @param xmlFile 开标一览表的xml文件对象
     * @return
     */
    protected JSONObject renderSchedule(File xmlFile) {
        if (!FileUtil.exist(xmlFile)) {
            return JSONUtil.createObj();
        }

        JSONObject result = JSONUtil.createObj(JSONConfig.create().setOrder(true));

        JSONObject xml = CommonUtil.xmlFileToJSON(xmlFile);
        if (CollUtil.isEmpty(xml)) {
            return JSONUtil.createObj();
        }

        JSONObject kbylXml = xml.getJSONObject("KBYLXml");
        if (CollUtil.isEmpty(kbylXml)) {
            return JSONUtil.createObj();
        }

        Set<String> keys = kbylXml.keySet();
        List<String> sortKeys = CollUtil.sort(keys, String::compareTo);
        for (String sortKey : sortKeys) {
            JSONObject kbylXmlJSONObject = kbylXml.getJSONObject(sortKey);
            JSONArray content = kbylXmlJSONObject.getJSONArray("content");

            result.putOpt(content.getStr(1), content.getStr(0));
        }

        return result;
    }
}
